/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import { cloneDeep, parseInt } from '@baize/wdk';
import { hvigorCore, TaskDetails } from '@ohos/hvigor';
import { TaskInputValue } from '@ohos/hvigor';
import fse from 'fs-extra';
import path from 'path';

import { projectOhosConfigManager } from '../common/global/project-ohos-config-manager.js';
import { ModulePathInfoIml } from '../common/iml/module-path-info-iml.js';
import { BuildDirConst } from '../const/build-directory-const.js';
import { DefaultTargetConst } from '../const/common-const.js';
import { ApiVersion } from '../const/sdk-const.js';
import { ProjectModel } from '../model/project/project-model.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';
import { AppEnvironmentOption } from '../options/configure/app_environment_option.js';

import { ModuleTargetData } from './data/hap-task-target-data.js';
import { TargetTaskService } from './service/target-task-service.js';
import { OhosHapTask } from './task/ohos-hap-task.js';
import ApiMeta = ProjectBuildProfile.ApiMeta;
import { AppOpt } from '../options/build/app-ohos-config.js';
import { Dependency } from '../project/dependency/core/dependency-interface.js';
import { HarTargetUtil } from '../utils/har-target-util.js';
import { InjectUtil } from '../utils/inject-util.js';
import { CmakeUtil } from '../utils/cmake-util.js';

import AppEnvironmentObj = AppEnvironmentOption.AppEnvironmentObj;

export const INPUT_KEY = {
  isHarModule: 'isHarModule',
  bundleName: 'bundleName',
  targetSdkVersion: 'targetSdkVersion',
  compatibleSdkVersion: 'compatibleSdkVersion',
  multiProjects: 'multiProjects',
  releaseType: 'releaseType',
  buildRoot: 'buildRoot',
  isDebug: 'isDebug',
  asanEnable: 'asanEnable',
  tsanEnable: 'tsanEnable',
  hwasanEnable: 'hwasanEnable',
  ubsanEnable: 'ubsanEnable',
  projectConfigAppOpt: 'projectConfigAppOpt',
  vendor: 'vendor',
  buildProfileAbilities: 'buildProfileAbilities',
  appEnvironments: 'appEnvironments',
  toolChainSdkVersion: 'toolChainSdkVersion',
  moduleJsonOpt: 'moduleJsonOpt',
  appJsonOpt: 'appJsonOpt',
};

/**
 * 合并module.json5或config.json
 *
 * @since 2022/9/15
 */
export abstract class AbstractMergeProfile extends OhosHapTask {
  protected _moduleTargetData: ModuleTargetData;
  protected _pathInfo: ModulePathInfoIml;
  protected _projectModel: ProjectModel;

  protected readonly _bundleName: string | undefined;
  protected readonly _targetName: string;
  protected readonly _buildRoot: string;
  protected readonly _vendor: string | undefined;
  protected readonly asanEnable: boolean;
  protected readonly tsanEnable: boolean;
  protected readonly hwAsanEnable: boolean;
  protected readonly ubsanEnable: boolean;
  protected readonly appEnvironments: string | undefined;

  protected constructor(taskService: TargetTaskService, taskDetails: TaskDetails) {
    super(taskService, taskDetails);
    const appOptFromConfig: AppOpt = cloneDeep(projectOhosConfigManager.getOverrides()?.appOpt);
    this._moduleTargetData = this.targetService.getTargetData();
    this._projectModel = this.service.getProjectModel();

    // 从product处获取bundleName
    this._bundleName = appOptFromConfig?.bundleName ?? this._moduleTargetData.getProduct().bundleName;

    // 从product处获取vendor名称
    this._vendor = appOptFromConfig?.vendor ?? this._moduleTargetData.getProduct().vendor;

    // 根据项目类型确定releaseType
    this._targetName = this._moduleTargetData.getTargetName();
    this._pathInfo = this._moduleTargetData.getPathInfo();
    this._buildRoot = this._pathInfo.getBuildRoot();

    const cmakeArgs: string[] = CmakeUtil.getCMakeArguments(taskService.getBuildOption().externalNativeOptions?.arguments);
    this.asanEnable = hvigorCore.getExtraConfig().get('ohos-debug-asan') === 'true' || cmakeArgs.includes('-DOHOS_ENABLE_ASAN=ON');
    this.tsanEnable = hvigorCore.getExtraConfig().get('ohos-enable-tsan') === 'true' || cmakeArgs.includes('-DOHOS_ENABLE_TSAN=ON');
    this.hwAsanEnable = hvigorCore.getExtraConfig().get('ohos-enable-hwasan') === 'true' || cmakeArgs.includes('-DOHOS_ENABLE_HWASAN=ON');
    this.ubsanEnable = hvigorCore.getExtraConfig().get('ohos-enable-ubsan') === 'true' || cmakeArgs.includes('-DOHOS_ENABLE_UBSAN=ON');
    this.appEnvironments = hvigorCore.getParameter().getExtParam('ohos-app-environment');
  }

  protected mergeDependsLibs(jsonType: string): string[] {
    // 三方依赖的module.json，包含hsp和har
    const ohDeps: string[] = this.service.getDependencyRootPaths();
    const localHarModuleMap = this.service.getHarModuleDependenciesWithRootPath();
    const localHarModulePathList: string[] = this.service.getHarModuleDependencyPaths();
    const localHspModuleList: string[] = this.service.getHspModuleDependencyPaths();
    return ohDeps.map((ohDepPath) => {
      // 根据用户配置的配置文件，获取本地依赖的build目录 如果没配置 则返回默认的路径
      // 如果是使用的本地依赖 则使用这个路径而不是ohDepPath
      const buildCacheParentDir = InjectUtil.getBuildCacheParentDir(
        ohDepPath,
        path.join(hvigorCore.getProject()!.getName(), ohDepPath.substring(hvigorCore.getProject()!.getNodeDir().length)),
      );

      if (localHspModuleList.includes(ohDepPath)) {
        const sameTargetHspPath = path.resolve(
          buildCacheParentDir,
          this._buildRoot,
          this.targetData.getProduct().name,
          BuildDirConst.INTERMEDIATES,
          BuildDirConst.INTERMEDIATES_MERGE_PROFILE,
          this._targetName,
          jsonType,
        );
        if (fse.pathExistsSync(sameTargetHspPath)) {
          return sameTargetHspPath;
        } else {
          return path.resolve(
            buildCacheParentDir,
            this._buildRoot,
            this.targetData.getProduct().name,
            BuildDirConst.INTERMEDIATES,
            BuildDirConst.INTERMEDIATES_MERGE_PROFILE,
            DefaultTargetConst.DEFAULT_TARGET,
            jsonType,
          );
        }
      } else if (localHarModulePathList.includes(ohDepPath)) {
        return this.getHarDenPath(localHarModuleMap, ohDepPath, buildCacheParentDir, jsonType);
      }
      return path.resolve(ohDepPath, BuildDirConst.SRC, BuildDirConst.MAIN, jsonType);
    });
  }

  getHarDenPath(localHarModuleMap: Map<string, [string, Dependency]>, ohDepPath: string, buildCacheParentDir: string, jsonType: string) {
    const harModuleDependency = localHarModuleMap.get(ohDepPath);
    const harDependencyPath = path.resolve(
      buildCacheParentDir,
      this._buildRoot,
      this.targetData.getProduct().name,
      BuildDirConst.INTERMEDIATES,
      BuildDirConst.INTERMEDIATES_MERGE_PROFILE,
      DefaultTargetConst.DEFAULT_TARGET,
      jsonType,
    );
    if (!harModuleDependency) {
      return harDependencyPath;
    }
    const targetName = HarTargetUtil.calDepHarTargets(this.targetService).get(harModuleDependency[0]) ?? 'default';
    const harTargetPath = path.resolve(
      buildCacheParentDir,
      this._buildRoot,
      this.targetData.getProduct().name,
      BuildDirConst.INTERMEDIATES,
      BuildDirConst.INTERMEDIATES_MERGE_PROFILE,
      targetName,
      jsonType,
    );
    if (fse.pathExistsSync(harTargetPath)) {
      return harTargetPath;
    } else {
      return harDependencyPath;
    }
  }

  apiTransform(api: ApiMeta) {
    if (api.version <= ApiVersion.API_VERSION_9) {
      return api.version;
    }
    let str = '';

    const msfNums: string[] = api.api.split('.');
    for (const s of msfNums) {
      str += s.padStart(2, '0');
    }
    str += `${api.version}`.padStart(3, '0');
    let index = 0;
    for (let i = 0; i < str.length; i++) {
      if (parseInt(str[i]) !== 0) {
        index = i;
        break;
      }
    }
    str = str.substring(index);
    return parseInt(str);
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputs = new Map<string, TaskInputValue>();
    if (this._bundleName) {
      inputs.set(INPUT_KEY.bundleName, this._bundleName);
    }
    if (this._vendor) {
      inputs.set(INPUT_KEY.vendor, this._vendor);
    }
    inputs.set(INPUT_KEY.targetSdkVersion, this.compileApiVersion);
    inputs.set(INPUT_KEY.compatibleSdkVersion, this.compatibleApiVersion);
    inputs.set(INPUT_KEY.releaseType, this.sdkInfo.getReleaseType());
    inputs.set(INPUT_KEY.buildRoot, this._buildRoot);
    inputs.set(INPUT_KEY.isDebug, this.targetService.isDebug());
    inputs.set(INPUT_KEY.asanEnable, this.asanEnable);
    inputs.set(INPUT_KEY.tsanEnable, this.tsanEnable);
    inputs.set(INPUT_KEY.hwasanEnable, this.hwAsanEnable);
    inputs.set(INPUT_KEY.ubsanEnable, this.ubsanEnable);
    if (this.appEnvironments) {
      inputs.set(INPUT_KEY.appEnvironments, this.appEnvironments);
    }
    const jsonProfileOpt = this.moduleModel.getJsonProfileOptByTargetName(this.targetName, this.isFaMode);
    inputs.set(INPUT_KEY.moduleJsonOpt, JSON.stringify(jsonProfileOpt));
    return inputs;
  }

  parseToAppEnvironments(jsonStr: string | undefined): AppEnvironmentObj[] {
    const appEnvironments: AppEnvironmentObj[] = [];
    if (jsonStr === undefined) {
      return appEnvironments;
    }

    // 去除两遍的空格和 括号
    const replacedStr = jsonStr.replace(/^\s*{\s*(.*)\s*}\s*$/, '$1');

    // 使用正则表达式匹配所有的键和值
    const regex = /(\w+):([^,]+)/g;
    let match;
    while ((match = regex.exec(replacedStr)) !== null) {
      const option: AppEnvironmentObj = {
        name: match[1],
        value: match[2].trim(),
      };
      appEnvironments.push(option);
    }
    return appEnvironments;
  }
}
